#include "SurfaceManager.h"
#include "LineManager.h"
#include "VertexManager.h"
#include "CurveManager.h"

CSurfaceManager::CSurfaceManager()
{
	m_pVertexManager = NULL;
	m_pLineManager = NULL;
	m_surfaces.clear();
}

CSurfaceManager::CSurfaceManager(CVertexManager* pVertexManager, CLineManager* pLineManager, CCurveManager* pCurveManager)
{
	m_pLineManager = pLineManager;
	m_pVertexManager = pVertexManager;
	m_pCurveManager = pCurveManager;
	m_surfaces.clear();
}

CSurfaceManager::~CSurfaceManager()
{
	m_pLineManager = NULL;
	m_pVertexManager = NULL;
	m_pCurveManager = NULL;
	m_surfaces.clear();
}

CSurface CSurfaceManager::GetSurface(int index)
{
	int count = GetSurfaceCount();
	index = (index >= 0) ? index : 0;
	index = (index < count) ? index : count - 1;
	return m_surfaces[index];
}

int CSurfaceManager::GetSurfaceCount()
{
	return m_surfaces.size();
}

void CSurfaceManager::InferSurface(int curveSegment)
{
	m_surfaces.clear();

	//	add line segments and vertex for curves
	int newVertexCount, newLineSegmentCount;
	SegmentAllCurves(curveSegment, newVertexCount, newLineSegmentCount);
	int currentLine = 0;
	CSurface surface;
	int lineSegmentCount = m_pLineManager->GetLineSegmentCount();
	CVertex *pStart, *pEnd;
	while (currentLine < lineSegmentCount)
	{
		surface.m_vertices.clear();
		surface.m_normal.SetValue(0, 0, 0);
		pStart = m_pLineManager->GetLineSegment(currentLine).m_start;
		pEnd = m_pLineManager->GetLineSegment(currentLine).m_end;
		surface.m_vertices.push_back(pStart->m_v3D);
		surface.m_vertices.push_back(pEnd->m_v3D);
		InferSurfaceFromCurrentLine(currentLine, currentLine, pStart, pEnd, surface);
		currentLine++;
	}
	TrimToSimpleSurface();
	//	delete newly added vertex and line segments
	for (int i = 0; i < newVertexCount; i++)
		m_pVertexManager->DeleteVertexFromHead();
	for (int i = 0; i < newLineSegmentCount; i++)
		m_pLineManager->DeleteLineFromHead();
}

void CSurfaceManager::InferSurfaceFromCurrentLine(
	int baseLine, 
	int currentLine, 
	CVertex* curStart,
	CVertex* curEnd,
	CSurface& surface
	)
{
	if ((int)surface.m_vertices.size() > 3)
	{
		for (int i = 0; i < (int)surface.m_vertices.size() - 1; i++)
		{
			if (curEnd->m_v3D == surface.m_vertices[i])
			{
				if (i == 0)
				{
					CSurface temp = surface;
					temp.m_normal.Normalize();
					temp.m_vertices.pop_back();
					m_surfaces.push_back(temp);
				}
				return;
			}
		}
	}
	int lineSegmentCount = m_pLineManager->GetLineSegmentCount();
	CVertex *pStart, *pEnd,  *p1;
	CVector3D v;
	for (int i = baseLine + 1; i < lineSegmentCount; i++)
	{
		pStart = m_pLineManager->GetLineSegment(i).m_start;
		pEnd = m_pLineManager->GetLineSegment(i).m_end;
		if ((i == currentLine) 
			|| (pStart != curEnd && pEnd != curEnd))
			continue;
		if (pStart == curEnd)
			p1 = pEnd;
		else if (pEnd == curEnd)
			p1 = pStart;
		//	a new candidate: curEnd -> p1
		v = p1->m_v3D - curEnd->m_v3D;
		if (IsParallel(v, curEnd->m_v3D - curStart->m_v3D)
			|| surface.m_normal.CalcLength() < SKE_ERROR_THRESHOLD
			|| abs((CVertex3D)v * surface.m_normal) < SKE_ERROR_THRESHOLD)
		{
			if (!IsParallel(curEnd->m_v3D - curStart->m_v3D, v)
				&& surface.m_normal.CalcLength() < SKE_ERROR_THRESHOLD)
			{
				surface.m_normal = Cross(curEnd->m_v3D - curStart->m_v3D, v);
			}
			surface.m_vertices.push_back((p1->m_v3D));
			InferSurfaceFromCurrentLine(baseLine, i, curEnd, p1, surface);
			surface.m_vertices.pop_back();
			int paraIndex;
			for (paraIndex = 0; paraIndex < (int)surface.m_vertices.size() - 2; paraIndex++)
			{
				if (!IsParallel(surface.m_vertices[paraIndex] - surface.m_vertices[paraIndex + 1],
					surface.m_vertices[paraIndex + 1] - surface.m_vertices[paraIndex + 2]))
					break;
			}
			if (paraIndex == surface.m_vertices.size() - 2)
			{
				surface.m_normal.SetValue(0, 0, 0);
			}
		}
	}
}

void CSurfaceManager::TrimToSimpleSurface()
{
	int i = 0;
	int lineCount = m_pLineManager->GetLineSegmentCount();
	CLineSegment line;
	CSurface surface;
	bool isSimpleSurface;
	while (i < (int)m_surfaces.size())
	{
		surface = m_surfaces[i];
		isSimpleSurface = true;
		for (int j = 0; j < lineCount && isSimpleSurface; j++)
		{
			line = m_pLineManager->GetLineSegment(j);
			if (IsInSurface(surface, line))
				isSimpleSurface = false;
		}
		if (!isSimpleSurface)
		{
			m_surfaces.erase(m_surfaces.begin() + i);
		}
		else
		{
			i++;
		}
	}
}

bool CSurfaceManager::IsInSurface(CSurface surface, CLineSegment line)
{
	if (surface.m_vertices.size() == 3)
		return false;

	int vertexCount = surface.m_vertices.size();
	int kStart, kEnd;
	kStart = kEnd = -1;
	for (int i = 0; i < vertexCount; i++)
	{
		if (line.m_start->m_v3D == surface.m_vertices[i])
			kStart = i;
		if (line.m_end->m_v3D == surface.m_vertices[i])
			kEnd = i;
		if (kStart != -1 && kEnd != -1)
			break;
	}
	if (kStart != -1 && kEnd != -1)
	{
		if (abs(kStart - kEnd) == 1
			|| (kStart == 0 && kEnd == vertexCount - 1)
			|| (kEnd == 0 && kStart == vertexCount - 1))
			return false;
		else
			return IsInSurface(surface, (line.m_start->m_v3D + line.m_end->m_v3D) / 2);
	}
	else if (kStart != -1)
		return IsInSurface(surface, line.m_end->m_v3D);
	else if (kEnd != -1)
		return IsInSurface(surface, line.m_start->m_v3D);
	else
		return IsInSurface(surface, line.m_start->m_v3D)
			&& IsInSurface(surface, line.m_start->m_v3D);
}

bool CSurfaceManager::IsInSurface(CSurface surface, CVertex3D v)
{
	CVector3D vec = v - surface.m_vertices[0];
	if (abs(v * surface.m_normal / (v.CalcLength() * surface.m_normal.CalcLength())) > SKE_ERROR_THRESHOLD)
		return false;
	float totalAngle = 0.0f, angle;
	int vertexCount = surface.m_vertices.size();
	CVector3D n, curN;
	int iNext;
	for (int i = 0; i < vertexCount; i++)
	{
		//	compute the angle between i and i+1
		iNext = i + 1;
		if (iNext == vertexCount)
			iNext = 0;
		angle = CalcAngle(surface.m_vertices[i] - v, surface.m_vertices[iNext] - v);
		curN = Cross(surface.m_vertices[i] - v, surface.m_vertices[iNext] - v);
		if (i == 0)
		{	
			n = curN;
			totalAngle += angle;
		}
		else
		{
			if (curN * n > SKE_ERROR_THRESHOLD)
				totalAngle += angle;
			else
				totalAngle -= angle;
		}
	}
	return abs(M_PI * 2 - totalAngle) < abs(totalAngle);
}

void CSurfaceManager::SegmentAllCurves(int curveSegment, int &newVertexCount, int &newLineSegmentCount)
{
	newVertexCount = m_pVertexManager->GetVertexCount();
	newLineSegmentCount = m_pLineManager->GetLineSegmentCount();

	int curveCount = m_pCurveManager->GetCurveCount();
	
	int* curveMatch = new int[curveCount];
	for (int i = 0; i < curveCount; i++)
		curveMatch[i] = -1;

	CCurve curve1, curve2;
	bool isReversed;
	float dist, minDist = -1;
	int minIndex;
	for (int i = 0; i < curveCount; i++)
	{
		if (curveMatch[i] != -1)
			continue;
		//	find the matched curves to curve i;
		curve1 = m_pCurveManager->GetCurve(i);
		minDist = -1;
		for (int j = i + 1; j < curveCount; j++)
		{
			curve2 = m_pCurveManager->GetCurve(j);
			if (m_pCurveManager->IsParallel(curve1, curve2, isReversed))
			{
				dist = CalcDistance(curve1.m_mid->m_v3D, curve2.m_mid->m_v3D);
				if (minDist < 0 || dist < minDist)
				{
					minDist = dist;
					minIndex = j;
				}
			}
		}
		if (minDist > 0)
		{
			//	we have found a matched curve
			curveMatch[i] = minIndex;
			curveMatch[minIndex] = i;
			curve2 = m_pCurveManager->GetCurve(minIndex);
			SegmentCurvePairs(curve1, curve2, curveSegment);
		}
	}
	delete []curveMatch;
	newVertexCount = m_pVertexManager->GetVertexCount() - newVertexCount;
	newLineSegmentCount = m_pLineManager->GetLineSegmentCount() - newLineSegmentCount;
}

void CSurfaceManager::SegmentCurvePairs(CCurve curve1, CCurve curve2, int curveSegment)
{
	bool isReversed;
	m_pCurveManager->IsParallel(curve1, curve2, isReversed);
	CVertex *p1Start, *p2Start, *p1End, *p2End;
	p1Start = curve1.m_start;
	p1End = curve1.m_end;
	p2Start = isReversed ? curve2.m_end : curve2.m_start;
	p2End = isReversed ? curve2.m_start : curve2.m_end;
	CVertex **v1, **v2;
	v1 = new CVertex *[curveSegment + 1];
	v2 = new CVertex *[curveSegment + 1];
	v1[0] = p1Start;
	v2[0] = p2Start;
	v1[curveSegment] = p1End;
	v2[curveSegment] = p2End;
	double t;
	CVertex newVertex;
	for (int i = 1; i < curveSegment; i++)
	{
		t = i * 1.0 / curveSegment;
		newVertex.m_v3D = p1Start->m_v3D * (1 - t) * (1 - t)
			+ curve1.m_mid->m_v3D * 2 * t * (1 - t)
			+ p1End->m_v3D * t * t;
		m_pVertexManager->AddVertexFromHead(newVertex);
		v1[i] = m_pVertexManager->FindVertex(newVertex);
		newVertex.m_v3D = p2Start->m_v3D * (1 - t) * (1 - t)
			+ curve2.m_mid->m_v3D * 2 * t * (1 - t)
			+ p2End->m_v3D * t * t;
		m_pVertexManager->AddVertexFromHead(newVertex);
		v2[i] = m_pVertexManager->FindVertex(newVertex);
	}
	CLineSegment newLine;
	for (int i = 1; i < curveSegment; i++)
	{
		newLine.m_start = v1[i];
		newLine.m_end = v2[i];
		m_pLineManager->AddLineFromHead(newLine);
		newLine.m_start = v1[i];
		newLine.m_end = v1[i - 1];
		m_pLineManager->AddLineFromHead(newLine);
		newLine.m_start = v2[i];
		newLine.m_end = v2[i - 1];
		m_pLineManager->AddLineFromHead(newLine);
	}
	newLine.m_start = v1[curveSegment - 1];
	newLine.m_end = v1[curveSegment];
	m_pLineManager->AddLineFromHead(newLine);
	newLine.m_start = v2[curveSegment - 1];
	newLine.m_end = v2[curveSegment];
	m_pLineManager->AddLineFromHead(newLine);
	delete []v1;
	delete []v2;
}